NOIP2009 普及组

T1:多项式输出

题目描述

一元 n 次多项式可用如下的表达式表示:

f(x)=anxn + an−1xn−1 +⋯+a1x + a0, an≠0

其中,aixi称为 i 次项,ai 称为 i 次项的系数。给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式:

  1. 多项式中自变量为 x ,从左到右按照次数递减顺序给出多项式。

  2. 多项式中只包含系数不为 0 的项。

  3. 如果多项式 n 次项系数为正,则多项式开头不出现“+”号,如果多项式 n 次项系数为负,则多项式以“-”号开头。

  4. 对于不是最高次的项,以“+”号或者“-”号连接此项与前一项,分别表示此项系数为正或者系数为负。紧跟一个正整数,表示此项系数的绝对值(如果一个高于 0 次的项,其系数的绝对值为 1 ,则无需输出 1 )。如果 x 的指数大于 1 ,则接下来紧跟的指数部分的形式为“xb”,其中 b x 的指数;如果 x 的指数为 1 ,则接下来紧跟的指数部分形式为“ x ”;如果 x 的指数为 0 ,则仅需输出系数即可。

  5. 多项式中,多项式的开头、结尾不含多余的空格。

输入格式

输入共有 2

第一行 1 个整数, n ,表示一元多项式的次数。

第二行有 n+1 个整数,其中第 i 个整数表示第 n−i+1 次项的系数,每两个整数之间用空格隔开。

输出格式

输出共 1 行,按题目所述格式输出多项式。

输入输出样例

输入 #1

5 
100 -1 1 -3 0 10

输出 #1

100x^5-x^4+x^3-3x^2+10

输入 #2

3 
-50 0 0 1 

输出 #2

-50x^3+1 

说明

NOIP 2009 普及组 第一题

对于 100\% 数据, 0≤n≤100 , −100≤ 系数 ≤100

题解:

​ 本题是一道模拟题。我们在读入第一项的系数之后,判断是否为负数,是负数的话那么先输出一个 "-" 号, 然后判断是否为 0 ,是的话就不输出了,不是的话再判断是否为 1 ,是 1 的话就不输出系数了,不是的话输出其绝对值。

​ 接着读入第 2 \sim n 项,如果等于 0 就啥都不输。不等于 0 的话,如果大于 0 输出 "+",然后如果其绝对值为 1 的话则不输出系数了,否则输出系数与 xn - i + 1就好辣~

#include <cmath>
#include <iostream>
using namespace std;
int coefficient[101];
int main() {
    int n;
    cin >> n;
    cin >> coefficient[1]; //读入第一项的系数
    if (coefficient[1] <= 0)
        cout << "-";
    if (coefficient[1] != 0) {
        if (abs(coefficient[1]) != 1)
            cout << abs(coefficient[1]);
        cout << "x^" << n;
    }
    for (int i = 2; i <= n; i++) {
        cin >> coefficient[i]; //读入每项的系数
        if (coefficient[i] != 0) {
            if (coefficient[i] > 0)
                cout << "+";
            if (abs(coefficient[i]) != 1)
                cout << coefficient[i];
            if (coefficient[i] == -1)
                cout << "-";
            cout << "x";
            if (n - i + 1 != 1)
                cout << "^" << n - i + 1;
        }
    }
    cin >> coefficient[n + 1];
    if (coefficient[n + 1] != 0) {
        if (coefficient[n + 1] > 0)
            cout << "+";
        cout << coefficient[n + 1] << endl;
    }
    return 0;
}

T2:分数线划定

题目描述

世博会志愿者的选拔工作正在 A 市如火如荼的进行。为了选拔最合适的人才,A市对所有报名的选手进行了笔试,笔试分数达到面试分数线的选手方可进入面试。面试分数线根据计划录取人数的 150\% 划定,即如果计划录取 m 名志愿者,则面试分数线为排名第 m×150\% (向下取整)名的选手的分数,而最终进入面试的选手为笔试成绩不低于面试分数线的所有选手。

现在就请你编写程序划定面试分数线,并输出所有进入面试的选手的报名号和笔试成绩。

输入格式

第一行,两个整数 n,m(5≤n≤5000,3≤m≤n) ,中间用一个空格隔开,其中 n 表示报名参加笔试的选手总数, m 表示计划录取的志愿者人数。输入数据保证 m×150\% 向下取整后小于等于 n

第二行到第 n+1 行,每行包括两个整数,中间用一个空格隔开,分别是选手的报名号 k(1000≤k≤9999) 和该选手的笔试成绩 s(1≤s≤100) 。数据保证选手的报名号各不相同。

输出格式

第一行,有 2 个整数,用一个空格隔开,第一个整数表示面试分数线;第二个整数为进入面试的选手的实际人数。

从第二行开始,每行包含 2 个整数,中间用一个空格隔开,分别表示进入面试的选手的报名号和笔试成绩,按照笔试成绩从高到低输出,如果成绩相同,则按报名号由小到大的顺序输出。

输入输出样例

输入 #1

6 3 
1000 90 
3239 88 
2390 95 
7231 84 
1005 95 
1001 88

输出 #1

88 5 
1005 95 
2390 95 
1000 90 
1001 88 
3239 88 

说明

【样例说明】

m×150\%=3×150\%=4.5 ,向下取整后为 4 。保证 4 个人进入面试的分数线为 88 ,但因为 88 有重分,所以所有成绩大于等于 88 的选手都可以进入面试,故最终有 5 个人进入面试。

NOIP 2009 普及组 第二题

题解:

​ 首先,我们先用 m × 1.5 计算出计划几个人能够进入面试。然后对选手的编号与成绩进行排序,排完序之后,我们找到面试分数线 score ,然后统计有多少人能够进入面试,统计完之后输出面试分数线与进面试的总人数,然后再找一遍,如果当前这个人的分数大于面试分数线,则输出这个人的编号与成绩。

最后附上代码:

#include <iostream>
using namespace std;
int a[5001][3]; //存储选手的报名号和选手的笔试成绩
int main() {
    int n, m;
    cin >> n >> m; //n表示报名参加笔试的选手总数,m表示计划录取的志愿者人数
    int people = m * 1.5; //几个人进入面试
    for (int i = 1; i <= n; i++)
        cin >> a[i][1] >> a[i][2];
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            if (a[i][1] > a[j][1]) { //编号小的在前面
                swap(a[i][1], a[j][1]);
                swap(a[i][2], a[j][2]);
            }
            if (a[i][2] < a[j][2]) { //按成绩进行交换
                swap(a[i][1], a[j][1]);
                swap(a[i][2], a[j][2]);
            }
        }
    }
    int score = a[people][2], sum = 0;
    for (int i = 1; i <= n; i++)
        if (a[i][2] >= score)
            sum++;
    cout << score << " " << sum << endl;
    for (int i = 1; i <= n; i++)
        if (a[i][2] >= score)
            cout << a[i][1] << " " << a[i][2] << endl;
    return 0;
}

T2:细胞分裂

题目描述

Hanks 博士是 BT ( Bio-Tech ,生物技术) 领域的知名专家。现在,他正在为一个细胞实验做准备工作:培养细胞样本。 Hanks 博士手里现在有 N 种细胞,编号从 1-N ,一个第 i 种细胞经过 1 秒钟可以分裂为 S_i 个同种细胞( S_i 为正整数)。现在他需要选取某种细胞的一个放进培养皿,让其自由分裂,进行培养。一段时间以后,再把培养皿中的所有细胞平均分入 M 个试管,形成 M 份样本,用于实验。 Hanks 博士的试管数 M 很大,普通的计算机的基本数据类型无法存储这样大的 M 值,但万幸的是, M 总可以表示为 m_1 m_2 次方,即 M = m_1^{m_2} ,其中 m_1,m_2 均为基本数据类型可以存储的正整数。 注意,整个实验过程中不允许分割单个细胞,比如某个时刻若培养皿中有 4 个细胞, Hanks 博士可以把它们分入 2 个试管,每试管内 2 个,然后开始实验。但如果培养皿中有 5 个细胞,博士就无法将它们均分入 2 个试管。此时,博士就只能等待一段时间,让细胞们继续分裂,使得其个数可以均分,或是干脆改换另一种细胞培养。 为了能让实验尽早开始, Hanks 博士在选定一种细胞开始培养后,总是在得到的细胞“刚好可以平均分入 M 个试管”时停止细胞培养并开始实验。现在博士希望知道,选择哪种细胞培养,可以使得实验的开始时间最早。

输入输出格式

输入格式

第一行,有一个正整数 N ,代表细胞种数。 第二行,有两个正整数 m_1,m_2 ,以一个空格隔开,即表示试管的总数 M = m_1^{m_2} . 第三行有 N 个正整数,第 i 个数 Si表示第 i 种细胞经过 1 秒钟可以分裂成同种细胞的个数。

输出格式

一个整数,表示从开始培养细胞到实验能够开始所经过的最少时间(单位为秒)。 如果无论 Hanks 博士选择哪种细胞都不能满足要求,则输出整数 -1

输入输出样例

输入 #1

1 
2 1 
3s

输出 #1

-1

输入 #2

2
24 1
30 12

输出 #2

2

说明

【输入输出说明】

经过 1 秒钟,细胞分裂成 3 个,经过 2 秒钟,细胞分裂成 9 个,……,可以看出无论怎么分裂,细胞的个数都是奇数,因此永远不能分入 2 个试管。

【输入输出样例 2 说明】

1 种细胞最早在 3 秒后才能均分入 24 个试管,而第 2 种最早在 2 秒后就可以均分(每试管 144/(241)=6 个)。故实验最早可以在 2 秒后开始。

【数据范围】

对于 50%的数据,有 m_1^{m_2} ≤ 30000

对于所有的数据,有 1 ≤N≤ 10000,1 ≤m_1 ≤ 30000,1 ≤m_2 ≤ 10000,1 ≤ S_i ≤ 2,000,000,000

NOIP 2009 普及组 第三题

题解:

​ 本题是一道比较有意思的数论题目。因为要可以平均分入 M 个试管我们便可以知道,如果 Si 符合条件,那么,Si 在经过 一段时间 t 之后,它一定 M 的倍数。因为 M\ = \ m_1^{m_2} ,所以 S_i^t 也一定是 m_1 的倍数,进而推得 S_i^t 包括所有 m_1 的因子, S_i 也包括 m_1 的所有因子。所以,我们便可以将 m_1 分解质因数,然后对于每一个 S_i ,都令 S_i\ mod m1 的所有因子,如果余数不为 0 ,则这个 S_i 不满足条件。那么,我们设 m_1 中的某个因子为 k ,则 M 包含 m_2 k ,所以我们将每个 m_1 的因子都乘上 m_2 ,便得到了 M 每个因子含有几个。

​ 那么,如果这个 S_i 满足条件应该怎么办呢?我们就来看它的时间是不是最少的。我们将 S_i 除以 M 的每一个因子,除到 S_i 不再包括这个因子。我们设这个因子为 k M 中含有 x k S_i 中含有 y k ,如果 y\ <\ x 的话,则可以将 M 表示为 k^x\ ×\ q S_i 表示为 k^y\ × \ p\ \ \ (q,\ p 均为正整数 ) 。那么,什么时候 S_i 包含的 k 的个数大于 M 包含的 k 的个数呢?无疑,再过 \lceil \frac{x}{y} \rceil 秒, S_i 包含的 k 的个数便大于 M 包含的 k 的个数了。然后,我们统计一下最大的 \lceil \frac{x}{y} \rceil ,便是这个 S_i 想要分到 M 个试管所需要的最小时间了。

​ 最后,我们再找一下所有 S_i 想要分到 M 个试管所需要的时间中最小的,然后输出就可以啦,如果没有一个 Si 符合条件,那么便输出 -1 结束程序。

最后附上代码:

#include <cmath>
#include <iostream>
using namespace std;
int t[30001]; //存储分解M的质因数的个数
int ym[30001]; //存储M的质因数
int k = 0;
void decomposition(int m) { //分解质因数
    bool r = false; //判断还有没有因数
    int n = sqrt(m) + 1;
    while (m > 0) {
        r = false;
        for (int i = 2; i <= n; i++) {
            if (m % i == 0) {
                if (t[i] == 0) {
                    k++;
                    ym[k] = i;
                }
                t[i]++;
                m /= i;
                r = true;
                break;
            }
        }
        if (r == false) {
            if (t[m] == 0 && m > 1) {
                k++;
                ym[k] = m;
            }
            t[m]++;
            return;
        }
    }
}
int maxx = 0;
int pd(int num) {
    int l = 0;
    for (int i = 1; i <= k; i++) {
        int sum = 0;
        while (!(num % ym[i])) {
            sum++;
            num /= ym[i];
        }
        if (!sum)
            return 0xfffffff;
        int w = 0;
        if (sum < t[ym[i]]) {
            if (t[ym[i]] % sum == 0)
                w = t[ym[i]] / sum;
            else
                w = t[ym[i]] / sum + 1;
        } else
            w = 1;
        l = max (l, w);
    }
    return l;
}
int main() {
    int n, m1, m2; //细胞种数与试管的总数
    cin >> n >> m1 >> m2;
    decomposition(m1);
    for (int i = 1; i <= k; i++)
        t[ym[i]] *= m2;
    int s;
    int ans = 0xfffffff;
    for (int i = 1; i <= n; i++) {
        cin >> s;
        ans = min(ans, pd(s));
    }
    if (ans == 0xfffffff)
        cout << -1;
    else
        cout << ans << endl;
    return 0;
}

T4:道路游戏

题目描述

小新正在玩一个简单的电脑游戏。 游戏中有一条环形马路,马路上有 n 个机器人工厂,两个相邻机器人工厂之间由一小段马路连接。小新以某个机器人工厂为起点,按顺时针顺序依次将这 n 个机器人工厂编号为 1-n ,因为马路是环形的,所以第 n 个机器人工厂和第 1 个机器人工厂是由一段马路连接在一起的。小新将连接机器人工厂的这 n 段马路也编号为 1-n ,并规定第 i 段马路连接第 i 个机器人工厂和第 i+1 个机器人工厂( 1≤i≤n-1 ),第 n 段马路连接第 n 个机器人工厂和第 1 个机器人工厂。 游戏过程中,每个单位时间内,每段马路上都会出现一些金币,金币的数量会随着时间发生变化,即不同单位时间内同一段马路上出现的金币数量可能是不同的。小新需要机器人的帮助才能收集到马路上的金币。所需的机器人必须在机器人工厂用一些金币来购买,机器人一旦被购买,便会沿着环形马路按顺时针方向一直行走,在每个单位时间内行走一次,即从当前所在的机器人工厂到达相邻的下一个机器人工厂,并将经过的马路上的所有金币收集给小新,例如,小新在 i 1≤i≤n )号机器人工厂购买了一个机器人,这个机器人会从 i 号机器人工厂开始,顺时针在马路上行走,第一次行走会经过 i 号马路,到达 i+1 号机器人工厂(如果 i=n ,机器人会到达第 1 个机器人工厂),并将 i 号马路上的所有金币收集给小新。 游戏中,环形马路上不能同时存在 2 个或者 2 个以上的机器人,并且每个机器人最多能够在环形马路上行走 p 次。小新购买机器人的同时,需要给这个机器人设定行走次数,行走次数可以为 1~p 之间的任意整数。当马路上的机器人行走完规定的次数之后会自动消失,小新必须立刻在任意一个机器人工厂中购买一个新的机器人,并给新的机器人设定新的行走次数。 以下是游戏的一些补充说明:

  1. 游戏从小新第一次购买机器人开始计时。
  2. 购买机器人和设定机器人的行走次数是瞬间完成的,不需要花费时间。
  3. 购买机器人和机器人行走是两个独立的过程,机器人行走时不能购买机器人,购买完机器人并且设定机器人行走次数之后机器人才能行走。
  4. 在同一个机器人工厂购买机器人的花费是相同的,但是在不同机器人工厂购买机器人的花费不一定相同。
  5. 购买机器人花费的金币,在游戏结束时再从小新收集的金币中扣除,所以在游戏过程中小新不用担心因金币不足,无法购买机器人而导致游戏无法进行。也因为如此,游戏结束后,收集的金币数量可能为负。 现在已知每段马路上每个单位时间内出现的金币数量和在每个机器人工厂购买机器人需要的花费,请你告诉小新,经过 m 个单位时间后,扣除购买机器人的花费,小新最多能收集到多少金币。

输入输出格式

输入格式

第一行 3 个正整数 n,m,p ,意义如题目所述。 接下来的 n 行,每行有 m 个正整数,每两个整数之间用一个空格隔开,其中第 i 行描 述了 i 号马路上每个单位时间内出现的金币数量( 1≤ 金币数量 ≤100 ),即第 i 行的第 j 1≤j≤m )个数表示第 j 个单位时间内 i 号马路上出现的金币数量。 最后一行,有 n 个整数,每两个整数之间用一个空格隔开,其中第 i 个数表示在 i 号机器人工厂购买机器人需要花费的金币数量( 1≤ 金币数量 ≤100 )。

输出格式

共一行,包含 1 个整数,表示在 m 个单位时间内,扣除购买机器人 花费的金币之后,小新最多能收集到多少金币。

输入输出样例

输入 #1

2 3 2 
1 2 3 
2 3 4 
1 2

输出 #1

5

【数据范围】 对于 40%的数据, 2≤n≤40,1≤m≤40

对于 90%的数据, 2≤n≤200,1≤m≤200

对于 100%的数据, 2≤n≤1000,1≤m≤1000,1≤p≤m

说明

NOIP 2009 普及组 第四题

题解:

​ 我们用 DP 数组枚举每个单位时间内小新最多能收集到多少金币,用 i 枚举时间,用 j 枚举工厂,用 k 枚举机器人走多长时间,用 q 存储钱数便可以得到: DP[k + i] = max(DP[k + i], q)

似乎这不是正解?管他呢,反正都卡过去了(逃

#include <iostream>
using namespace std;
int money[1001][1001]; //马路上出现的金币数量
int cost[1001]; //机器人工厂购买机器人需要花费的金币数量
int DP[1001]; //枚举每个单位时间内小新最多能收集到多少金币
int main() {
    int n, m, p; //n个机器人工厂,m为总时间,每个机器人最多能够在环形马路上行走p次
    int q = 0;
    cin >> n >> m >> p;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> money[i][j];
    for (int i = 1; i <= n; i++)
        cin >> cost[i];
    for (int i = 1; i <= m; i++)
        DP[i] = -123456;
    DP[1] = money[1][1] - cost[1];
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= n; j++) {
            q = DP[i - 1] - cost[j];
            for (int k = 0; k < p && k + i <= m; k++) {
                int w = k + j > n ? (k + j) % n : k + j;
                q += money[w][i + k];
                DP[k + i] = max(DP[k + i], q);
            }
        }
    cout << DP[m] << endl;
    return 0;
}